﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows.Input;
using ScrumFactory.Composition.ViewModel;
using ScrumFactory.Composition;
using ScrumFactory.Services;
using System.Configuration;
using ScrumFactory.Composition.View;


namespace ScrumFactory.Login.ViewModel {


    /// <summary>
    /// Login view model.
    /// </summary>
    [Export(typeof(LoginViewModel))]
    [Export(typeof(IStartsWithApp))]
    public class LoginViewModel : BasePanelViewModel, IStartsWithApp, IViewModel, INotifyPropertyChanged {


        private IAuthorizationService authorizator;
        private IEventAggregator aggregator;
        private IBackgroundExecutor executor;
        private IDialogService dialogs;

        private IServerUrl serverUrl;

        private IDialogViewModel window;

        private bool signing = false;

        [ImportingConstructor]
        public LoginViewModel(
            [Import(typeof(IEventAggregator))] IEventAggregator aggregator,
            [Import(typeof(IBackgroundExecutor))] IBackgroundExecutor executor,
            [Import(typeof(IAuthorizationService))] IAuthorizationService authorizator,
            [Import(typeof(IDialogService))] IDialogService dialogService,
            [Import] IServerUrl serverUrl,
            [ImportMany(typeof(IAuthorizationProviderService))] IEnumerable<IAuthorizationProviderService> providers) {

                this.aggregator = aggregator;
                this.executor = executor;
                this.authorizator = authorizator;
                this.dialogs = dialogService;
                this.serverUrl = serverUrl;

            
                AllProviders = providers;

                aggregator.Subscribe(ScrumFactoryEvent.ShellStarted, () => {
                    SetLoginFields();
                    SignIn();
                });
                aggregator.Subscribe(ScrumFactoryEvent.ShowLogin, Show);
                aggregator.Subscribe<string>(ScrumFactoryEvent.ServerArgOnInit, OnSetupServer);
                        
                SignInCommand = new DelegateCommand(CanSignIn, SignIn);

                ServerUrl = Properties.Settings.Default.serverUrl;
                            
              


            
        }

        private void OnSetupServer(string server) {
            this.ServerUrl = server;
            Properties.Settings.Default.Save();
        }

        public IEnumerable<IAuthorizationProviderService> AllProviders { get; private set; }

        
        

        public string ServerUrl { 
            get {
                return serverUrl.Url;
            }
            set {
                serverUrl.Url = value;
                Properties.Settings.Default.serverUrl = value;
                OnPropertyChanged("ServerUrl");
            }
        }

       
        private string titleMessage;
        public string TitleMessage {
            get {
                return titleMessage;
            }
            set {
                titleMessage = value;
                OnPropertyChanged("TitleMessage");
            }
        }

        public bool RemindUser {
            get {
                return Properties.Settings.Default.remindUser;
            }
            set {
                Properties.Settings.Default.remindUser = value;
            }
        }

        public bool Signing {
            get {
                return signing;
            }
            set {
                signing = value;
                OnPropertyChanged("Signing");
                aggregator.Publish<bool>(ScrumFactoryEvent.Signing, signing);
            }
        }


        public string LastUser {
            get {
                return Properties.Settings.Default.lastUser;
            }
            private set {
                Properties.Settings.Default.lastUser = value;
            }
        }


        public string LastProvider {
            get {
                return Properties.Settings.Default.lastProvider;
            }
            private set {
                Properties.Settings.Default.lastProvider = value;
            }
        }

        public string LastPassword {
            get {
                return DecryptString(Properties.Settings.Default.lastPassword);
            }
            set {
                Properties.Settings.Default.lastPassword = EncryptString(value);
            }
        }

        private IAuthorizationProviderService selectedProvider;
        public IAuthorizationProviderService SelectedProvider {
            get {
                return selectedProvider;
            }
            set {
                selectedProvider = value;
                if (IsNetworkUser)
                    User = UserNetworkName;
                OnPropertyChanged("SelectedProvider");
                OnPropertyChanged("IsUserFieldReadOnly");
            }
        }

        private string userNetworkName;
        private string UserNetworkName {
            get {
                if (userNetworkName != null)
                    return userNetworkName;
                try {
                    userNetworkName = System.DirectoryServices.AccountManagement.UserPrincipal.Current.UserPrincipalName;
                }
                catch (Exception) {
                    userNetworkName = "";
                }
                return userNetworkName;
            }
        }

        private void AfterGetAuthorizationToken(string token) {

            Signing = false;
            ((DelegateCommand)SignInCommand).NotifyCanExecuteChanged();
                        
            if (token == null) {
                TitleMessage = Properties.Resources.Sign_in;
                LoginStatusMessage = Properties.Resources.Invalid_credentials;
                ((LoginDialog)View).ResetPassword(); // get rid of password
                if(!View.IsVisible) Show(true);
                return;
            }

            // validates token with Scrum Factory Server
            MemberProfile myProfile = null;
            try {
                myProfile = authorizator.SignInMember(Provider.ProviderName, User, token);
            } catch(Exception) {}

            if (myProfile == null) {
                TitleMessage = Properties.Resources.Sign_in;
                LoginStatusMessage = Properties.Resources.Factory_could_not_validate_credentials;
                ((LoginDialog)View).ResetPassword(); // get rid of password
                if (!View.IsVisible) Show();
                return;
            }

            TitleMessage = Properties.Resources.Sign_in;

            if (RemindUser) {
                
                LastUser = User;
                LastProvider = SelectedProvider.ProviderName;
                LastPassword = Password;
            }


            Properties.Settings.Default.Save();

            User = null;
            LoginStatusMessage = null;

            OnPropertyChanged("IsMemberSigned");
            OnPropertyChanged("SignedMemberProfile");

            if(View.IsVisible)
                window.Close();

            aggregator.Publish<MemberProfile>(ScrumFactoryEvent.SignedMemberChanged, authorizator.SignedMemberProfile);

        }

        private IAuthorizationProviderService Provider {
            get {
                if (AllProviders == null || SelectedProvider==null)
                    return null;
                return AllProviders.Where(p => p.ProviderName == SelectedProvider.ProviderName).SingleOrDefault();                
            }
        }

        private bool CanSignIn() {
            return !Signing; 
        }

        private void SignIn() {

            if (Provider == null || string.IsNullOrEmpty(User) || string.IsNullOrEmpty(Password)) {
                Show();
                return;
            }

            if (View.IsVisible)
                window.Close();
            
            TitleMessage = Properties.Resources.Signing;

            Signing = true;


            executor.StartBackgroundTask<string>(
                    () => {
                        return Provider.GetAuthorizationToken(User, Password);
                    },
                    AfterGetAuthorizationToken
                    ,
                    ex => {
                        Signing = false;
                        ((DelegateCommand)SignInCommand).NotifyCanExecuteChanged();
                        TitleMessage = Properties.Resources.Sign_in;
                        if(ex is ScrumFactory.Exceptions.ScrumFactoryException) {
                            ScrumFactory.Exceptions.ScrumFactoryException sfEx = ex as ScrumFactory.Exceptions.ScrumFactoryException;
                            sfEx.LocalizeException(Properties.Resources.ResourceManager);
                            LoginStatusMessage = Properties.Resources.Error_on_provider + ":" + sfEx.LocalizedMessage;
                        }
                        else
                            LoginStatusMessage = Properties.Resources.Error_on_provider + ":" + ex.Message;
                        if (!View.IsVisible) Show(true);
                    }
                 );
            
            
        }

        public bool IsNetworkUser {
            get {
                if (Provider == null)
                    return false;
                return Provider.IsNetworkUser;
            }
        }

        public bool IsPasswordRequired {
            get {
                if (Provider == null)
                    return false;
                return Provider.PasswordRequired;
            }
        }

       

        private void SignOut() {
            authorizator.SignOutMember(
                authorizator.SignedMemberProfile.AuthorizationProvider,
                authorizator.SignedMemberProfile.MemberUId);
            aggregator.Publish<MemberProfile>(ScrumFactoryEvent.SignedMemberChanged, null);
        }

        private string user;
        public string User {
            get {                
                return user;
            }
            set {
                user = value;
                OnPropertyChanged("User");
            }
        }

        private string password;
        public string Password {
            get {
                return password;
            }
            set {
                password = value;                
                OnPropertyChanged("Password");
            }
        }

        private string loginStatusMessage;
        public string LoginStatusMessage {
            get {
                return loginStatusMessage;
            }
            set {
                loginStatusMessage = value;
                OnPropertyChanged("LoginStatusMessage");
            }
        }

        public System.Windows.Input.ICommand SignInCommand {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the view.
        /// </summary>
        /// <value>The view.</value>        
        [Import(typeof(LoginDialog))]
        public IView View { get; set; }


        public void Show(bool afterFailed) {

            if (View.IsVisible)
                return;

            if (authorizator.SignedMemberProfile != null)
                SignOut();

            if(!afterFailed)
                SetLoginFields();
            
            window = dialogs.NewDialog(TitleMessage, View);
            window.Show();

        }

        public void Show() {
            Show(false);
        }


        private void SetLoginFields() {
            TitleMessage = Properties.Resources.Sign_in;
            if (RemindUser) {
                SelectedProvider = AllProviders.SingleOrDefault(p => p.ProviderName == LastProvider);
                User = LastUser;
                ((LoginDialog)View).InitPassword(LastPassword);
            }

            if (SelectedProvider == null)
                SelectedProvider = AllProviders.FirstOrDefault();
        }

        
        private string EncryptString(string str) {
            return str;
        }

        private string DecryptString(string str) {
            return str;
        }

    }

}
